package cn.lliiooll.bk;

import org.bukkit.configuration.MemorySection;
import org.bukkit.configuration.file.YamlConfiguration;

import java.io.File;
import java.lang.reflect.*;
import java.util.*;

/**
 * @author lliiooll
 */
public class YamlHelper {

    /**
     * 从yaml返回一个对象
     *
     * @param fileName 文件名称
     * @param clazz    要返回的类
     * @return clazz的对象
     * @throws Exception 可能抛出文件不存在等错误
     */
    public static <T> T read(String fileName, Class<T> clazz) throws Exception {
        return read(new File(fileName), clazz);

    }

    /**
     * 从yaml返回一个对象
     *
     * @param file  文件
     * @param clazz 要返回的类
     * @return clazz的对象
     * @throws Exception 可能抛出文件不存在等错误
     */
    public static <T> T read(File file, Class<T> clazz) throws Exception {
        return read(YamlConfiguration.loadConfiguration(file), clazz);

    }

    /**
     * 从yaml返回一个对象
     *
     * @param yaml  yml文件
     * @param clazz 要返回的类
     * @return clazz的对象
     * @throws Exception 可能抛出文件不存在等错误
     */
    public static <T> T read(YamlConfiguration yaml, Class<T> clazz) throws Exception {
        return read(yaml, clazz, "");
    }

    /**
     * 从yaml返回一个对象
     *
     * @param yaml  yml文件
     * @param clazz 要返回的类
     * @param path  要开始读取的路径
     * @return clazz的对象
     * @throws Exception 可能抛出文件不存在等错误
     */
    public static <T> T read(YamlConfiguration yaml, Class<T> clazz, String path) throws Exception {
        return process(null, yaml, clazz, null, path, false);
    }

    public static void write(String fileName, Object object) throws Exception {
        write(new File(fileName), object);
    }

    public static void write(File file, Object object) throws Exception {
        write(file, object, "");
    }

    public static void write(File file, Object object, String path) throws Exception {
        process(file, null, null, object, path, true);
    }

    /**
     * 处理 序列化/反序列化
     *
     * @param file     yml文件
     * @param yaml     传入null
     * @param clazz    要反序列化的对象
     * @param instance 要序列化的对象
     * @param path     路径
     * @param write    true为序列化,false为反序列化
     * @return 序列化为null，反序列化为对象
     */
    private static <T> T process(final File file, YamlConfiguration yaml, final Class<T> clazz, final Object instance, String path, final boolean write) throws Exception {
        if (yaml == null) yaml = YamlConfiguration.loadConfiguration(file);// 赋值
        if (write) { // 序列化(写入)
            if (instance != null) {// 判断不为null继续
                for (Field field : instance.getClass().getDeclaredFields()) {// 遍历所有变量
                    if (Modifier.isFinal(field.getModifiers()) || Modifier.isStatic(field.getModifiers()))// 如果是常量或静态变量
                        continue; // 跳过
                    field.setAccessible(true);// 避免读写private变量出错
                    YamlField yi = field.getDeclaredAnnotation(YamlField.class);// 获取注解
                    String mpath = field.getName();// 拼接路径
                    if (yi != null) {
                        mpath = yi.value().equalsIgnoreCase("") ? mpath : yi.value();
                    }
                    String p = path == null ? mpath : (path + "." + mpath);// 拼接路径
                    Object i = field.get(instance);// 获取变量中的对象
                    if (YamlHelper.isPrimitive(i)) {// 基本变量
                        yaml.set(p, i);// 设置值
                        // System.out.println(p + " => " + i);
                        continue;// 继续
                    }
                    // 不是基本变量
                    //存在问题: Map和List的判断

                    if (YamlHelper.isNoSupport(field.getType())) {// 支持list & map
                        Type type = field.getGenericType();
                        if (type instanceof ParameterizedType) {
                            ParameterizedType pt = (ParameterizedType) type;
                            // 得到泛型里的class类型对象
                            Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
                            if (pt.getActualTypeArguments().length > 1) {
                                actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[1];
                            }
                            if (YamlHelper.isPrimitive(actualTypeArgument)) {
                                yaml.set(p, i);// 设置值
                                //  System.out.println(p + " => " + i);
                            }
                        }
                        continue;// 继续
                    }
                    process(file, yaml, clazz, i, p, write);// 继续写入
                }
                for (Method method : instance.getClass().getDeclaredMethods()) {// 遍历所有方法
                    method.setAccessible(true);// 避免读写private变量出错
                    YamlField yi = method.getDeclaredAnnotation(YamlField.class);// 获取注解
                    String mpath = method.getName();// 拼接路径
                    if (yi == null || !yi.wmethod()) {
                        continue;
                    }
                    mpath = yi.value().equalsIgnoreCase("") ? mpath : yi.value();
                    String p = path == null ? mpath : (path + "." + mpath);// 拼接路径
                    //System.out.println(method);
                    Object i = method.invoke(instance);// 获取方法中的对象
                    if (YamlHelper.isPrimitive(i)) {// 方法返回类是否为基本类
                        yaml.set(p, i);// 设置值
                        // System.out.println(p + " => " + i);
                        continue;// 继续
                    }
                    // 不是基本变量
                    //存在问题: Map和List的判断


                    if (YamlHelper.isNoSupport(method.getReturnType())) {// 支持map
                        Type type = method.getGenericReturnType();
                        if (type instanceof ParameterizedType) {
                            ParameterizedType pt = (ParameterizedType) type;
                            // 得到泛型里的class类型对象
                            Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
                            if (pt.getActualTypeArguments().length > 1) {
                                actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[1];
                            }
                            if (YamlHelper.isPrimitive(actualTypeArgument)) {
                                yaml.set(p, i);// 设置值
                                //System.out.println(p + " => " + i);
                            }
                        }
                        continue;// 继续
                    }
                    process(file, yaml, clazz, i, p, write);// 继续写入
                }
            }
            yaml.save(file);// 序列化完毕，保存
            return null;// 返回空值
        }
        //反序列化
        T ins = instance == null ? clazz.newInstance() : (T) instance;
        // 判断不为null继续
        for (Field field : ins.getClass().getDeclaredFields()) {// 遍历所有变量
            if (Modifier.isFinal(field.getModifiers()) || Modifier.isStatic(field.getModifiers()))// 如果是常量或静态变量
                continue; // 跳过
            field.setAccessible(true);// 避免读写private变量出错
            YamlField yi = field.getDeclaredAnnotation(YamlField.class);// 获取注解
            String mpath = field.getName();// 拼接路径
            if (yi != null) {
                mpath = yi.value().equalsIgnoreCase("") ? mpath : yi.value();
            }
            String p = path == null ? mpath : (path + "." + mpath);// 拼接路径
            if (YamlHelper.isPrimitive(field.getType())) {// 基本变量
                Object v = yaml.get(p);
                if (v != null)
                    field.set(ins, v);
                // System.out.println(field.getName() + " <= (" + p + ")@" + yaml.get(p));
                continue;// 继续
            }
            // 不是基本变量
            //存在问题: Map和List的判断

            if (YamlHelper.isNoSupport(field.getType())) {// 支持list & map
                Type type = field.getGenericType();
                if (type instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) type;
                    // 得到泛型里的class类型对象
                    Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
                    if (pt.getActualTypeArguments().length > 1) {
                        actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[1];
                    }
                    if (YamlHelper.isPrimitive(actualTypeArgument)) {
                        Object v = yaml.get(p);
                        if (v != null)
                            if (v instanceof MemorySection) {
                                field.set(ins, YamlHelper.toMap((MemorySection) v));
                            } else
                                field.set(ins, v);
                        //System.out.println(field.getName() + " <= (" + p + ")@" + yaml.get(p));
                        //  System.out.println(p + " => " + i);
                    }
                }
                continue;// 继续
            }
            field.set(ins, process(file, yaml, field.getType(), null, p, write));// 继续写入
        }
        for (Method method : ins.getClass().getDeclaredMethods()) {// 遍历所有方法
            method.setAccessible(true);// 避免读写private方法出错
            YamlField yi = method.getDeclaredAnnotation(YamlField.class);// 获取注解
            String mpath = method.getName();// 拼接路径
            if (yi == null || !yi.rmethod()) {
                continue;
            }
            mpath = yi.value().equalsIgnoreCase("") ? mpath : yi.value();
            String p = path == null ? mpath : (path + "." + mpath);// 拼接路径
            if (YamlHelper.isPrimitive(method.getReturnType())) {// 方法返回类是否为基本类
                Object[] o = new Object[method.getParameterCount() < 1 ? 1 : method.getParameterCount()];
                o[0] = yaml.get(p);
                method.invoke(ins, o);
                //System.out.println(method.getName() + " <= (" + p + ")@" + yaml.get(p));
                continue;// 继续
            }
            // 不是基本变量
            //存在问题: Map和List的判断


            if (YamlHelper.isNoSupport(method.getReturnType())) {// 支持map
                Type type = method.getGenericReturnType();
                if (type instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) type;
                    // 得到泛型里的class类型对象
                    Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
                    if (pt.getActualTypeArguments().length > 1) {
                        actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[1];
                    }
                    if (YamlHelper.isPrimitive(actualTypeArgument)) {
                        Object v = yaml.get(p);
                        if (v != null)
                            if (v instanceof MemorySection) {
                                method.invoke(ins, YamlHelper.toMap((MemorySection) v));
                            } else
                                method.invoke(ins, v);
                        System.out.println(method.getName() + " <= (" + p + ")@" + yaml.get(p));
                        //System.out.println(p + " => " + i);
                    }
                }
                continue;// 继续
            }
            method.invoke(ins, process(file, yaml, method.getReturnType(), null, p, write));
        }
        return ins;
    }

    private static Map<?, ?> toMap(MemorySection v) {

        try {
            Field f = v.getClass().getField("map");
            f.setAccessible(true);
            return (Map<?, ?>) f.get(v);
        } catch (Exception e) {
            return new HashMap<>();
        }
    }

    private static boolean isNoSupport(Class<?> c) {
        return c == List.class || c == Map.class;
    }

    public static boolean isPrimitive(Class<?> c) {
        try {
            return c.isPrimitive() || ((Class<?>) c.getDeclaredField("TYPE").get(null)).isPrimitive();
        } catch (Exception e) {
            return c == String.class;
        }
    }

    public static boolean isPrimitive(Object o) {
        return isPrimitive(o.getClass());
    }
}
